/**
 * 
 */
package com.cmcc.awareness;

import java.net.InetAddress;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.pcap4j.packet.IpV4Packet;
import org.pcap4j.packet.IpV6Packet;
import org.pcap4j.packet.Packet;
import org.pcap4j.packet.TcpPacket;
import org.pcap4j.packet.UdpPacket;
import org.pcap4j.packet.namednumber.EtherType;

import jpcap.packet.TCPPacket;
import jpcap.packet.UDPPacket;

/**
 * @author Amish
 * @date 2018年12月7日 下午7:14:27
 * 
 * IP操作符表达式：SERVER/SERVER1
 * 
 * IPv4 & IPv6(IPv6只有一个IP地址)
 * 
 *  eg:  
    SERVER:192.168.1.1|0
	SERVER:192.168.1.1-192.168.1.254|0
	SERVER:192.168.1.1/24|0
	SERVER:192.168.1.1:80,8080,5670~7650|0
	SERVER1:1080:0:0:0:8:800:200C:417A

 * 
 */
public class ProtoDatProtocolFeatureCodeElement_SERVER implements ProtoDatProtocolFeatureCodeElement {
	private static Logger logger = LogManager.getLogger(ProtoDatProtocolFeatureCodeElement_SERVER.class);

	String initialStr;
	//IPV4
	String server;//(1)独立一个IP地址 (2)有-，表示IP段 (3)有/表示IP前缀地址段，其中mask范围为24-31
	String[] server_ports;//80,8080,5670~7650,遍历判断时需要检查是否有~
	
	//IPV6
	String server1;
	
	public String appName;
	public String protocolType;
	public ProtoDatProtocolFeatureCodeElement_SERVER(String appName,String protocolType){
		this.appName = appName;
		this.protocolType = protocolType;
	}
	
	@Override
	public String getType() {
		return "SERVER";
	}


	@Override
	public boolean distinguish(String protocolType,jpcap.packet.Packet packet) {
		try{
			InetAddress packetSrcIp;
			InetAddress packetDstIp;
			String packetSrcPort;
			String packetDstPort;
			if("TCP".equals(protocolType)){
				TCPPacket tp = (TCPPacket) packet;
				packetSrcIp = tp.src_ip;
				packetDstIp = tp.dst_ip;
				packetSrcPort = String.valueOf(tp.src_port);
			    packetDstPort =  String.valueOf(tp.dst_port);	
			}else if("UDP".equals(protocolType)){
				UDPPacket up = (UDPPacket) packet;
				packetSrcIp = up.src_ip;
				packetDstIp = up.dst_ip;
				packetSrcPort = String.valueOf(up.src_port);
			    packetDstPort =  String.valueOf(up.dst_port);
			}else{
				return false;
			}
			
			if( packetSrcIp == null || packetDstIp == null )
				return false;
			
			if(this.server != null){//IPv4
				if(this.server.contains("/")){//(3)
					if(this.ipInPrefix(packetSrcIp.getHostAddress(), this.server) 
							|| this.ipInPrefix(packetDstIp.getHostAddress(), this.server)){
						return true;
					}
				}else if(this.server.contains("-")){//(2)
					if(this.ipInSection(packetSrcIp.getHostAddress(), this.server) 
							|| this.ipInSection(packetDstIp.getHostAddress(), this.server)){
						return true;
					}				
				}else{//(1)
					if(this.server_ports != null){//有端口号
						boolean havePort = false;
						for(String port : this.server_ports){
							if(port.contains("~")){
								String portL = port.split("\\~")[0];
								String portR = port.split("\\~")[1];
								int portLInt = Integer.valueOf(portL);
								int portRInt = Integer.valueOf(portR);
								int packetSrcPortInt = Integer.valueOf(packetSrcPort);
								int packetDstPortInt = Integer.valueOf(packetDstPort);
								havePort = havePort || (portLInt <= packetSrcPortInt && packetSrcPortInt  <= portRInt) ||  (portLInt <= packetDstPortInt && packetDstPortInt  <= portRInt);
							}else{
								havePort = havePort || packetSrcPort.equalsIgnoreCase(port) || packetDstPort.equalsIgnoreCase(port); 
							}
						}
						return (this.server.equalsIgnoreCase(packetSrcIp.getHostAddress()) 
								|| this.server.equalsIgnoreCase(packetSrcIp.getHostAddress())) && havePort;
						
						
					}else{//无端口号
						if(this.server.equalsIgnoreCase(packetSrcIp.getHostAddress()) 
								|| this.server.equalsIgnoreCase(packetDstIp.getHostAddress())){
							return true;
						}
					}
				}
				
			}else{//IPv6
				if(this.server1.equalsIgnoreCase(packetSrcIp.getHostAddress()) 
						|| this.server1.equalsIgnoreCase(packetDstIp.getHostAddress())){
					return true;
				}
			}
			
			return false;
		}catch(Exception e){
			e.printStackTrace();
			System.out.println("Distinguish Exception -- <"+this.appName+">"+"<"+this.protocolType+"> : "+this.toString());
			return false;
		}

	}
	
	/**
	 * (IPv4)判断IP是否属于某个IP地址范围
	 * @param ip  要判断的IP地址。eg：192.168.0.2
	 * @param ipSection  IP地址范围。eg：192.168.0.1-192.168.0.200
	 * @return
	 */
	public boolean ipInSection(String ip,String ipSection){

		if (ipSection == null)   {
			//throw new NullPointerException("IP段不能为空！");
			System.out.println("ip is null");
			return false;
		}
            
        if (ip == null){
        	//throw new NullPointerException("IP不能为空！");
        	System.out.println("ipSection is null");
        	return false;
        }
            	
        ipSection = ipSection.trim();   
        ip = ip.trim();   
        final String REGX_IP = "((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";   
        final String REGX_IPB = REGX_IP + "\\-" + REGX_IP;   
        if (!ipSection.matches(REGX_IPB) || !ip.matches(REGX_IP))   
            return false;   
        int idx = ipSection.indexOf('-');   
        String[] sips = ipSection.substring(0, idx).split("\\.");   
        String[] sipe = ipSection.substring(idx + 1).split("\\.");   
        String[] sipt = ip.split("\\.");   
        long ips = 0L, ipe = 0L, ipt = 0L;   
        for (int i = 0; i < 4; ++i) {   
            ips = ips << 8 | Integer.parseInt(sips[i]);   
            ipe = ipe << 8 | Integer.parseInt(sipe[i]);   
            ipt = ipt << 8 | Integer.parseInt(sipt[i]);   
        }   
        if (ips > ipe) {   
            long t = ips;   
            ips = ipe;   
            ipe = t;   
        }   
        return ips <= ipt && ipt <= ipe;
	}
	
	/**
	 * (IPv4)判断IP是否属于某个网段
	 * @param ip  要判断的IP地址。eg：192.168.0.2
	 * @param ipNetworkSegment  网段。eg：192.168.0.1/24
	 * @return
	 */
	public boolean ipInPrefix(String ip, String ipNetworkSegment) {
		String[] ips = ip.split("\\.");
		int ipAddr = (Integer.parseInt(ips[0]) << 24)
				| (Integer.parseInt(ips[1]) << 16)
				| (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]);
		int type = Integer.parseInt(ipNetworkSegment.replaceAll(".*/", ""));
		int mask = 0xFFFFFFFF << (32 - type);
		String cidrIp = ipNetworkSegment.replaceAll("/.*", "");
		String[] cidrIps = cidrIp.split("\\.");
		int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24)
				| (Integer.parseInt(cidrIps[1]) << 16)
				| (Integer.parseInt(cidrIps[2]) << 8)
				| Integer.parseInt(cidrIps[3]); 
		
		return (ipAddr & mask) == (cidrIpAddr & mask);
	}


	@Override
	public void parseSelf(String element) {
		try{
			this.initialStr = element;
			if(element.startsWith("SERVER")){//IPv4
				String[] strs = element.split("\\|");
				String[] subStrs = strs[0].split("\\:");
				
				this.server = subStrs[1];
				if(subStrs.length == 3 ){//有端口号
					this.server_ports = subStrs[2].split("\\,");
				}
				
			}else{//IPv6
				this.server1 = element.substring(element.indexOf(":") + 1);//去掉SERVER1：
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	public String toString(){
		return initialStr;
	}

	@Override
	public boolean distinguish(String protocolType, Packet packet) {
		try{
			InetAddress packetSrcIp;
			InetAddress packetDstIp;
			String packetSrcPort;
			String packetDstPort;
			if("TCP".equals(protocolType)){
				TcpPacket tp = (TcpPacket) packet;
				org.pcap4j.packet.EthernetPacket ethernetPacket = packet.get(org.pcap4j.packet.EthernetPacket.class);
                EtherType eth_type = ethernetPacket.getHeader().getType();
                if(eth_type == EtherType.IPV4)
                {
                    IpV4Packet ipv4_packet = packet.get(IpV4Packet.class);
                    packetSrcIp  = ipv4_packet.getHeader().getSrcAddr();
                    packetDstIp = ipv4_packet.getHeader().getDstAddr();
                }else {
                	IpV6Packet ipv6_packet = packet.get(IpV6Packet.class);
                    packetSrcIp  = ipv6_packet.getHeader().getSrcAddr();
                    packetDstIp = ipv6_packet.getHeader().getDstAddr();
                }

				packetSrcPort = String.valueOf(tp.getHeader().getSrcPort());
			    packetDstPort =  String.valueOf(tp.getHeader().getDstPort());	
			}else if("UDP".equals(protocolType)){
				UdpPacket up = (UdpPacket) packet;
				org.pcap4j.packet.EthernetPacket ethernetPacket = packet.get(org.pcap4j.packet.EthernetPacket.class);
                EtherType eth_type = ethernetPacket.getHeader().getType();
                if(eth_type == EtherType.IPV4)
                {
                    IpV4Packet ipv4_packet = packet.get(IpV4Packet.class);
                    packetSrcIp  = ipv4_packet.getHeader().getSrcAddr();
                    packetDstIp = ipv4_packet.getHeader().getDstAddr();
                }else {
                	IpV6Packet ipv6_packet = packet.get(IpV6Packet.class);
                    packetSrcIp  = ipv6_packet.getHeader().getSrcAddr();
                    packetDstIp = ipv6_packet.getHeader().getDstAddr();
                }
				packetSrcPort = String.valueOf(up.getHeader().getSrcPort());
			    packetDstPort =  String.valueOf(up.getHeader().getDstPort());
			}else{
				return false;
			}
			
			if( packetSrcIp == null || packetDstIp == null )
				return false;
			
			if(this.server != null){//IPv4
				if(this.server.contains("/")){//(3)
					if(this.ipInPrefix(packetSrcIp.getHostAddress(), this.server) 
							|| this.ipInPrefix(packetDstIp.getHostAddress(), this.server)){
						return true;
					}
				}else if(this.server.contains("-")){//(2)
					if(this.ipInSection(packetSrcIp.getHostAddress(), this.server) 
							|| this.ipInSection(packetDstIp.getHostAddress(), this.server)){
						return true;
					}				
				}else{//(1)
					if(this.server_ports != null){//有端口号
						boolean havePort = false;
						for(String port : this.server_ports){
							if(port.contains("~")){
								String portL = port.split("\\~")[0];
								String portR = port.split("\\~")[1];
								int portLInt = Integer.valueOf(portL);
								int portRInt = Integer.valueOf(portR);
								int packetSrcPortInt = Integer.valueOf(packetSrcPort);
								int packetDstPortInt = Integer.valueOf(packetDstPort);
								havePort = havePort || (portLInt <= packetSrcPortInt && packetSrcPortInt  <= portRInt) ||  (portLInt <= packetDstPortInt && packetDstPortInt  <= portRInt);
							}else{
								havePort = havePort || packetSrcPort.equalsIgnoreCase(port) || packetDstPort.equalsIgnoreCase(port); 
							}
						}
						return (this.server.equalsIgnoreCase(packetSrcIp.getHostAddress()) 
								|| this.server.equalsIgnoreCase(packetSrcIp.getHostAddress())) && havePort;
						
						
					}else{//无端口号
						if(this.server.equalsIgnoreCase(packetSrcIp.getHostAddress()) 
								|| this.server.equalsIgnoreCase(packetDstIp.getHostAddress())){
							return true;
						}
					}
				}
				
			}else{//IPv6
				if(this.server1.equalsIgnoreCase(packetSrcIp.getHostAddress()) 
						|| this.server1.equalsIgnoreCase(packetDstIp.getHostAddress())){
					return true;
				}
			}
			
			return false;
		}catch(Exception e){
			e.printStackTrace();
			System.out.println("Distinguish Exception -- <"+this.appName+">"+"<"+this.protocolType+"> : "+this.toString());
			return false;
		}
	}

}
